home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / msdos / raytrace / pov / bin / xtras / csg.c < prev    next >
C/C++ Source or Header  |  1994-09-11  |  16KB  |  606 lines

  1. /****************************************************************************
  2. *
  3. *  ATTENTION!!!
  4. *
  5. *  THIS FILE HAS BEEN MODIFIED!!! IT IS NOT PART OF THE OFFICAL
  6. *  POV-RAY 2.2 DISTRIBUTION!!!
  7. *
  8. *  THIS FILE IS PART OF "FASTER THAN POV-RAY" (VERSION 2.2),
  9. *  A SPED-UP VERSION OF POV-RAY 2.2. USE AT YOUR OWN RISK!!!!!!
  10. *
  11. *  New files: addon0.c, addon1.c, addon2.c, addon3.c, addon.h
  12. *
  13. *  The additional modules were written by Dieter Bayer.
  14. *
  15. *  Send comments, suggestions, bugs, ideas ... to:
  16. *
  17. *  e-mail: dieter@cip.e-technik.uni-erlangen.de
  18. *  CIS: 100255.3074
  19. *
  20. *  All changed/added lines are enclosed in #ifdef DB_CODE ... #endif
  21. *
  22. *  The vista projection was taken from:
  23. *
  24. *    A. Hashimoto, T. Akimoto, K. Mase, and Y. Suenaga, 
  25. *    "Vista Ray-Tracing: High Speed Ray Tracing Using Perspective
  26. *    Projection Image", New Advances in Computer Graphics, Proceedings
  27. *    of CG International '89, R. A. Earnshaw, B. Wyvill (Eds.), 
  28. *    Springer, ..., pp. 549-560
  29. *
  30. *  The idea for the light buffer was taken from:
  31. *
  32. *    E. Haines and D. Greenberg, "The Light Buffer: A Shadow-Testing 
  33. *    Accelerator", IEEE CG&A, Vol. 6, No. 9, Sept. 1986, pp. 6-16
  34. *
  35. *****************************************************************************/
  36.  
  37. /****************************************************************************
  38. *                   csg.c
  39. *
  40. *  This module implements routines for constructive solid geometry.
  41. *
  42. *  from Persistence of Vision Raytracer
  43. *  Copyright 1993 Persistence of Vision Team
  44. *---------------------------------------------------------------------------
  45. *  NOTICE: This source code file is provided so that users may experiment
  46. *  with enhancements to POV-Ray and to port the software to platforms other
  47. *  than those supported by the POV-Ray Team.  There are strict rules under
  48. *  which you are permitted to use this file.  The rules are in the file
  49. *  named POVLEGAL.DOC which should be distributed with this file. If
  50. *  POVLEGAL.DOC is not available or for more info please contact the POV-Ray
  51. *  Team Coordinator by leaving a message in CompuServe's Graphics Developer's
  52. *  Forum.  The latest version of POV-Ray may be found there as well.
  53. *
  54. * This program is based on the popular DKB raytracer version 2.12.
  55. * DKBTrace was originally written by David K. Buck.
  56. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
  57. *
  58. *****************************************************************************/
  59.  
  60. #include "frame.h"
  61. #include "vector.h"
  62. #include "povproto.h"
  63. #ifdef DB_CODE
  64. /* Changes necessary for better bounding box calculation of intersections. */
  65. #include "addon.h"
  66.  
  67. extern METHODS Plane_Methods;
  68. #endif
  69.  
  70. METHODS CSG_Union_Methods =
  71.   { 
  72.   All_CSG_Union_Intersections,
  73.   Inside_CSG_Union, NULL /*Normal*/,
  74.   Copy_CSG,
  75.   Translate_CSG, Rotate_CSG,
  76.   Scale_CSG, Transform_CSG, Invert_CSG_Union, Destroy_CSG
  77. };
  78.  
  79. METHODS CSG_Merge_Methods =
  80.   { 
  81.   All_CSG_Merge_Intersections,
  82.   Inside_CSG_Union, NULL /*Normal*/,
  83.   Copy_CSG,
  84.   Translate_CSG, Rotate_CSG,
  85.   Scale_CSG, Transform_CSG, Invert_CSG_Union, Destroy_CSG
  86. };
  87.  
  88. METHODS CSG_Intersection_Methods =
  89.   { 
  90.   All_CSG_Intersect_Intersections,
  91.   Inside_CSG_Intersection, NULL /*Normal*/,
  92.   Copy_CSG,
  93.   Translate_CSG, Rotate_CSG,
  94.   Scale_CSG, Transform_CSG, Invert_CSG_Intersection, Destroy_CSG
  95. };
  96.  
  97. int All_CSG_Union_Intersections (Object, Ray, Depth_Stack)
  98. OBJECT *Object;
  99. RAY *Ray;
  100. ISTACK *Depth_Stack;
  101.   {
  102.   register int Found;
  103.   OBJECT *Current_Sib, *Clip;
  104.   ISTACK *Local_Stack;
  105.   INTERSECTION *Sibling_Intersection;
  106.  
  107.   Found = FALSE;
  108.  
  109.   if ((Clip = Object->Clip) == NULL) /* Use shortcut if no clip */
  110.     {
  111.     for (Current_Sib = ((CSG *)Object)->Children;
  112.          Current_Sib != NULL ;
  113.          Current_Sib = Current_Sib->Sibling)
  114.       if (Ray_In_Bounds (Ray, Current_Sib->Bound))
  115.         if (All_Intersections (Current_Sib, Ray, Depth_Stack))
  116.           Found = TRUE;
  117.     }
  118.   else
  119.     {
  120.     Local_Stack = open_istack ();
  121.  
  122.     for (Current_Sib = ((CSG *)Object)->Children;
  123.          Current_Sib != NULL ;
  124.          Current_Sib = Current_Sib->Sibling)
  125.       if (Ray_In_Bounds (Ray, Current_Sib->Bound))
  126.         if (All_Intersections (Current_Sib, Ray, Local_Stack))
  127.           while ((Sibling_Intersection = pop_entry(Local_Stack)) != NULL)
  128.             if (Point_In_Clip (&Sibling_Intersection->IPoint, Clip))
  129.               {
  130.               push_copy (Depth_Stack, Sibling_Intersection);
  131.               Found = TRUE;
  132.               }
  133.     close_istack (Local_Stack);
  134.     }
  135.   return (Found);
  136.   }
  137.  
  138. int All_CSG_Intersect_Intersections (Object, Ray, Depth_Stack)
  139. OBJECT *Object;
  140. RAY *Ray;
  141. ISTACK *Depth_Stack;
  142.   {
  143.   int Maybe_Found, Found;
  144.   OBJECT *Current_Sib, *Inside_Sib;
  145.   ISTACK *Local_Stack;
  146.   INTERSECTION *Sibling_Intersection;
  147.  
  148.   Local_Stack = open_istack ();
  149.  
  150.   Found = FALSE;
  151.  
  152.   for (Current_Sib = ((CSG *)Object)->Children;
  153.   Current_Sib != NULL;
  154.   Current_Sib = Current_Sib->Sibling) 
  155.     {
  156.     if (Ray_In_Bounds (Ray, Current_Sib->Bound))
  157.       if (All_Intersections (Current_Sib, Ray, Local_Stack))
  158.         while ((Sibling_Intersection = pop_entry(Local_Stack)) != NULL)
  159.           {
  160.           Maybe_Found = TRUE;
  161.           for (Inside_Sib = ((CSG *)Object)->Children;
  162.           Inside_Sib != NULL;
  163.           Inside_Sib = Inside_Sib->Sibling)
  164.             if (Inside_Sib != Current_Sib)
  165.               if (!Inside_Object (&Sibling_Intersection->IPoint, Inside_Sib)) 
  166.                 {
  167.                 Maybe_Found = FALSE;
  168.                 break;
  169.                 }
  170.           if (Maybe_Found)
  171.             if (Point_In_Clip (&Sibling_Intersection->IPoint, Object->Clip))
  172.               {
  173.               push_copy(Depth_Stack, Sibling_Intersection);
  174.               Found = TRUE;
  175.               }
  176.           }
  177.     }
  178.   close_istack (Local_Stack);
  179.   return (Found);
  180.   }
  181.  
  182. int All_CSG_Merge_Intersections (Object, Ray, Depth_Stack)
  183. OBJECT *Object;
  184. RAY *Ray;
  185. ISTACK *Depth_Stack;
  186.   {
  187.   register int Found, inside_flag;
  188.   OBJECT *Sib1, *Sib2;
  189.   ISTACK *Local_Stack;
  190.   INTERSECTION *Sibling_Intersection;
  191.  
  192.   Found = FALSE;
  193.  
  194.   Local_Stack = open_istack ();
  195.  
  196.   for (Sib1 = ((CSG *)Object)->Children;
  197.        Sib1 != NULL ;
  198.        Sib1 = Sib1->Sibling)
  199.     if (Ray_In_Bounds (Ray, Sib1->Bound))
  200.       if (All_Intersections (Sib1, Ray, Local_Stack))
  201.         while ((Sibling_Intersection = pop_entry (Local_Stack)) !=  NULL)
  202.           if (Point_In_Clip (&Sibling_Intersection->IPoint,Object->Clip)) 
  203.           {
  204.           inside_flag = TRUE;
  205.           for (Sib2 = ((CSG *)Object)->Children;
  206.                Sib2 != NULL && inside_flag == TRUE;
  207.                Sib2 = Sib2->Sibling)
  208.             if (Sib1 != Sib2)
  209.               if (Inside_Object(&Sibling_Intersection->IPoint,Sib2))
  210.                 inside_flag = FALSE;
  211.           if (inside_flag == TRUE) 
  212.             {
  213.             Found = TRUE;
  214.             push_copy (Depth_Stack, Sibling_Intersection);
  215.             }
  216.           }
  217.   close_istack (Local_Stack);
  218.   return (Found);
  219.   }
  220.  
  221. int Inside_CSG_Union (IPoint, Object)
  222. VECTOR *IPoint;
  223. OBJECT *Object;
  224.   {
  225.   OBJECT *Current_Sib;
  226.  
  227.   for (Current_Sib = ((CSG *)Object)->Children;
  228.   Current_Sib != NULL ;
  229.   Current_Sib = Current_Sib->Sibling)
  230.     if (Inside_Object (IPoint, Current_Sib))
  231.       return (TRUE);
  232.  
  233.   return (FALSE);
  234.   }
  235.  
  236. int Inside_CSG_Intersection (IPoint, Object)
  237. OBJECT *Object;
  238. VECTOR *IPoint;
  239.   {
  240.   OBJECT *Current_Sib;
  241.  
  242.   for (Current_Sib = ((CSG *)Object)->Children;
  243.   Current_Sib != NULL ;
  244.   Current_Sib = Current_Sib->Sibling)
  245.     if (!Inside_Object (IPoint, Current_Sib))
  246.       return (FALSE);
  247.  
  248.   return (TRUE);
  249.   }
  250.  
  251. void *Copy_CSG (Object)
  252. OBJECT *Object;
  253.   {
  254.   CSG *New;
  255.   OBJECT *Old_Sib, *New_Sib, *Prev_Sib;
  256.  
  257.   if ((New = (CSG *) malloc (sizeof (CSG))) == NULL)
  258.     MAError ("CSG");
  259.  
  260. #ifdef DB_CODE
  261.   /* Changes necessary for better bounding box calculation of intersections. */
  262.   /* Make sure that all entries of the old csg is copied */
  263.   *New = *(CSG *)Object;
  264. #endif
  265.  
  266.   New->Children = Prev_Sib = NULL;
  267.  
  268.   for (Old_Sib = ((CSG *)Object)->Children;
  269.   Old_Sib != NULL ;
  270.   Old_Sib = Old_Sib->Sibling) 
  271.     {
  272.     New_Sib = Copy_Object (Old_Sib);
  273.  
  274.     if (New->Children == NULL)
  275.       New->Children = New_Sib;
  276.  
  277.     if (Prev_Sib != NULL)
  278.       Prev_Sib->Sibling = New_Sib;
  279.  
  280.     Prev_Sib = New_Sib;
  281.     }
  282.  
  283.   return (New);
  284.   }
  285.  
  286. void Translate_CSG (Object, Vector)
  287. OBJECT *Object;
  288. VECTOR *Vector;
  289.   {
  290.   OBJECT *Sib;
  291. #ifdef DB_CODE
  292.   /* Changes necessary for better bounding box calculation of intersections. */
  293.   TRANSFORM Trans;
  294. #endif
  295.  
  296.   for (Sib = ((CSG *) Object)->Children;
  297.   Sib != NULL ;
  298.   Sib = Sib->Sibling)
  299.     Translate_Object (Sib, Vector);
  300. #ifdef DB_CODE
  301.   /* Changes necessary for better bounding box calculation of intersections. */
  302.   /* Compute_CSG_Bounds must not be used here!!! */
  303.   Compute_Translation_Transform(&Trans, Vector);
  304.   recompute_bbox(&Object->Bounds, &Trans);
  305. #else
  306.   Compute_CSG_Bounds(Object);
  307. #endif
  308.   }
  309.  
  310. void Rotate_CSG (Object, Vector)
  311. OBJECT *Object;
  312. VECTOR *Vector;
  313.   {
  314.   OBJECT *Sib;
  315. #ifdef DB_CODE
  316.   /* Changes necessary for better bounding box calculation of intersections. */
  317.   TRANSFORM Trans;
  318. #endif
  319.  
  320.   for (Sib = ((CSG *) Object)->Children;
  321.   Sib != NULL ;
  322.   Sib = Sib->Sibling)
  323.     Rotate_Object (Sib, Vector);
  324. #ifdef DB_CODE
  325.   /* Changes necessary for better bounding box calculation of intersections. */
  326.   /* Compute_CSG_Bounds must not be used here!!! */
  327.   Compute_Rotation_Transform(&Trans, Vector);
  328.   recompute_bbox(&Object->Bounds, &Trans);
  329. #else
  330.   Compute_CSG_Bounds(Object);
  331. #endif
  332.   }
  333.  
  334. void Scale_CSG (Object, Vector)
  335. OBJECT *Object;
  336. VECTOR *Vector;
  337.   {
  338.   OBJECT *Sib;
  339. #ifdef DB_CODE
  340.   /* Changes necessary for better bounding box calculation of intersections. */
  341.   TRANSFORM Trans;
  342. #endif
  343.  
  344.   for (Sib = ((CSG *) Object)->Children;
  345.   Sib != NULL ;
  346.   Sib = Sib->Sibling)
  347.     Scale_Object (Sib, Vector);
  348. #ifdef DB_CODE
  349.   /* Changes necessary for better bounding box calculation of intersections. */
  350.   /* Compute_CSG_Bounds must not be used here!!! */
  351.   Compute_Scaling_Transform(&Trans, Vector);
  352.   recompute_bbox(&Object->Bounds, &Trans);
  353. #else
  354.   Compute_CSG_Bounds(Object);
  355. #endif
  356.   }
  357.  
  358. void Transform_CSG (Object, Trans)
  359. OBJECT *Object;
  360. TRANSFORM *Trans;
  361.   {
  362.   OBJECT *Sib;
  363.  
  364.   for (Sib = ((CSG *) Object)->Children;
  365.   Sib != NULL ;
  366.   Sib = Sib->Sibling)
  367.     Transform_Object (Sib, Trans);
  368.   Compute_CSG_Bounds(Object);
  369.   }
  370.  
  371. void Destroy_CSG (Object)
  372. OBJECT *Object;
  373.   {
  374.   Destroy_Object (((CSG *) Object)->Children);
  375.   free (Object);
  376.   }
  377.  
  378. void Invert_CSG_Union (Object)
  379. OBJECT *Object;
  380.   {
  381.   OBJECT *Sib;
  382.  
  383.   Object->Methods = &CSG_Intersection_Methods;
  384.  
  385.   for (Sib = ((CSG *)Object)->Children;
  386.   Sib != NULL ;
  387.   Sib = Sib->Sibling)
  388.     Invert_Object (Sib);
  389. #ifdef DB_CODE
  390.   /* Changes necessary for better bounding box calculation of intersections. */
  391.   ((CSG *)Object)->Inverted ^= TRUE;
  392. #endif
  393.   }
  394.  
  395. void Invert_CSG_Intersection (Object)
  396. OBJECT *Object;
  397.   {
  398.   OBJECT *Sib;
  399.  
  400.   Object->Methods = &CSG_Union_Methods;
  401.  
  402.   for (Sib = ((CSG *)Object)->Children;
  403.   Sib != NULL ;
  404.   Sib = Sib->Sibling)
  405.     Invert_Object (Sib);
  406. #ifdef DB_CODE
  407.   /* Changes necessary for better bounding box calculation of intersections. */
  408.   ((CSG *)Object)->Inverted ^= TRUE;
  409. #endif
  410.   }
  411.  
  412. CSG *Create_CSG_Union ()
  413.   {
  414.   CSG *New;
  415.  
  416.   if ((New = (CSG *) malloc (sizeof (CSG))) == NULL)
  417.     MAError ("union");
  418.  
  419.   INIT_OBJECT_FIELDS(New, UNION_OBJECT, &CSG_Union_Methods)
  420.  
  421.     New->Children = NULL;
  422. #ifdef DB_CODE
  423.   /* Changes necessary for better bounding box calculation of intersections. */
  424.   New->Inverted = FALSE;
  425. #endif
  426.   return (New);
  427.   }
  428.  
  429. CSG *Create_CSG_Merge ()
  430.   {
  431.   CSG *New;
  432.  
  433.   if ((New = (CSG *) malloc (sizeof (CSG))) == NULL)
  434.     MAError ("merge");
  435.  
  436.   INIT_OBJECT_FIELDS(New, MERGE_OBJECT, &CSG_Merge_Methods)
  437.  
  438.     New->Children = NULL;
  439. #ifdef DB_CODE
  440.   /* Changes necessary for better bounding box calculation of intersections. */
  441.   New->Inverted = FALSE;
  442. #endif
  443.   return (New);
  444.   }
  445.  
  446. CSG *Create_CSG_Intersection ()
  447.   {
  448.   CSG *New;
  449.  
  450.   if ((New = (CSG *) malloc (sizeof (CSG))) == NULL)
  451.     MAError ("intersection");
  452.  
  453.   INIT_OBJECT_FIELDS(New, INTERSECTION_OBJECT, &CSG_Intersection_Methods)
  454.  
  455.     New->Children = NULL;
  456. #ifdef DB_CODE
  457.   /* Changes necessary for better bounding box calculation of intersections. */
  458.   New->Inverted = FALSE;
  459. #endif
  460.   return (New);
  461.   }
  462.  
  463. void Compute_CSG_Bounds (Object)
  464. OBJECT *Object;
  465.   {
  466. #ifdef DB_CODE
  467.   /* Changes necessary for better bounding box calculation of intersections. */
  468.   DBL Old_Volume, New_Volume;
  469.   VECTOR NewMin, NewMax, TmpMin, TmpMax;
  470.   OBJECT *Sib;
  471.  
  472.   if (Object->Methods == &CSG_Intersection_Methods)
  473.   {
  474.     /* Calculate the bounding box of a CSG intersection object
  475.        by intersecting the bounding boxes of all children. */
  476.  
  477.     NewMin.x = NewMin.y = NewMin.z = -BOUND_HUGE;
  478.     NewMax.x = NewMax.y = NewMax.z = +BOUND_HUGE;
  479.  
  480.     for (Sib = ((CSG *)Object)->Children; Sib != NULL; Sib = Sib->Sibling)
  481.     {
  482.       /* Inverted objects mustn't be considered */
  483.  
  484.       if (!Test_Inverted(Sib))
  485.       {
  486.     if (Sib->Methods == &Plane_Methods)
  487.     {
  488.       Compute_Plane_Min_Max((PLANE *)Sib, &TmpMin, &TmpMax);
  489.     }
  490.     else
  491.     {
  492.       TmpMin.x = Sib->Bounds.Lower_Left.x;
  493.       TmpMin.y = Sib->Bounds.Lower_Left.y;
  494.       TmpMin.z = Sib->Bounds.Lower_Left.z;
  495.       TmpMax.x = Sib->Bounds.Lower_Left.x + Sib->Bounds.Lengths.x;
  496.       TmpMax.y = Sib->Bounds.Lower_Left.y + Sib->Bounds.Lengths.y;
  497.       TmpMax.z = Sib->Bounds.Lower_Left.z + Sib->Bounds.Lengths.z;
  498.     }
  499.  
  500.     NewMin.x = max(NewMin.x, TmpMin.x);
  501.     NewMin.y = max(NewMin.y, TmpMin.y);
  502.     NewMin.z = max(NewMin.z, TmpMin.z);
  503.     NewMax.x = min(NewMax.x, TmpMax.x);
  504.     NewMax.y = min(NewMax.y, TmpMax.y);
  505.     NewMax.z = min(NewMax.z, TmpMax.z);
  506.       }
  507.     }
  508.   }
  509.   else
  510.   {
  511.     /* Calculate the bounding box of a CSG merge/union object. */
  512.  
  513.     NewMin.x = NewMin.y = NewMin.z = +BOUND_HUGE;
  514.     NewMax.x = NewMax.y = NewMax.z = -BOUND_HUGE;
  515.  
  516.     for (Sib = ((CSG *)Object)->Children; Sib != NULL; Sib = Sib->Sibling)
  517.     {
  518.       if (Sib->Methods == &Plane_Methods)
  519.       {
  520.     Compute_Plane_Min_Max((PLANE *)Sib, &TmpMin, &TmpMax);
  521.       }
  522.       else
  523.       {
  524.     TmpMin.x = Sib->Bounds.Lower_Left.x;
  525.     TmpMin.y = Sib->Bounds.Lower_Left.y;
  526.     TmpMin.z = Sib->Bounds.Lower_Left.z;
  527.     TmpMax.x = Sib->Bounds.Lower_Left.x + Sib->Bounds.Lengths.x;
  528.     TmpMax.y = Sib->Bounds.Lower_Left.y + Sib->Bounds.Lengths.y;
  529.     TmpMax.z = Sib->Bounds.Lower_Left.z + Sib->Bounds.Lengths.z;
  530.       }
  531.  
  532.       NewMin.x = min(NewMin.x, TmpMin.x);
  533.       NewMin.y = min(NewMin.y, TmpMin.y);
  534.       NewMin.z = min(NewMin.z, TmpMin.z);
  535.       NewMax.x = max(NewMax.x, TmpMax.x);
  536.       NewMax.y = max(NewMax.y, TmpMax.y);
  537.       NewMax.z = max(NewMax.z, TmpMax.z);
  538.     }
  539.   }
  540.  
  541.   if ((NewMin.x > NewMax.x) || (NewMin.y > NewMax.y) || (NewMin.z > NewMax.z))
  542.   {
  543.     Warn("Degenerate CSG bounding box (not used!)\n",1.0);
  544.   }
  545.   else
  546.   {
  547.     New_Volume = (NewMax.x - NewMin.x) *
  548.          (NewMax.y - NewMin.y) *
  549.          (NewMax.z - NewMin.z);
  550.  
  551.     BOUNDS_VOLUME(Old_Volume, Object->Bounds);
  552.  
  553.     if (New_Volume < Old_Volume)
  554.     {
  555.       Object->Bounds.Lower_Left = NewMin;
  556.       VSub (Object->Bounds.Lengths, NewMax, NewMin);
  557.  
  558.       /* Beware of lengths too large */
  559.  
  560.       if (Object->Bounds.Lengths.x > CRITICAL_LENGTH)
  561.       {
  562.     Object->Bounds.Lower_Left.x = -BOUND_HUGE/2;
  563.     Object->Bounds.Lengths.x = BOUND_HUGE;
  564.       }
  565.       if (Object->Bounds.Lengths.y > CRITICAL_LENGTH)
  566.       {
  567.     Object->Bounds.Lower_Left.y = -BOUND_HUGE/2;
  568.     Object->Bounds.Lengths.y = BOUND_HUGE;
  569.       }
  570.       if (Object->Bounds.Lengths.z > CRITICAL_LENGTH)
  571.       {
  572.     Object->Bounds.Lower_Left.z = -BOUND_HUGE/2;
  573.     Object->Bounds.Lengths.z = BOUND_HUGE;
  574.       }
  575.     }
  576.   }
  577. #else
  578.   VECTOR mins, maxs;
  579.   DBL tmin, tmax;
  580.   OBJECT *Sib;
  581.  
  582.   Make_Vector(&mins,  BOUND_HUGE,  BOUND_HUGE,  BOUND_HUGE);
  583.   Make_Vector(&maxs, -BOUND_HUGE, -BOUND_HUGE, -BOUND_HUGE);
  584.  
  585.   for (Sib = ((CSG *) Object)->Children;
  586.   Sib != NULL ;
  587.   Sib = Sib->Sibling)
  588.     {
  589.     tmin = Sib->Bounds.Lower_Left.x;
  590.     tmax = Sib->Bounds.Lower_Left.x + Sib->Bounds.Lengths.x;
  591.     if (tmin < mins.x) mins.x = tmin;
  592.     if (tmax > maxs.x) maxs.x = tmax;
  593.     tmin = Sib->Bounds.Lower_Left.y;
  594.     tmax = Sib->Bounds.Lower_Left.y + Sib->Bounds.Lengths.y;
  595.     if (tmin < mins.y) mins.y = tmin;
  596.     if (tmax > maxs.y) maxs.y = tmax;
  597.     tmin = Sib->Bounds.Lower_Left.z;
  598.     tmax = Sib->Bounds.Lower_Left.z + Sib->Bounds.Lengths.z;
  599.     if (tmin < mins.z) mins.z = tmin;
  600.     if (tmax > maxs.z) maxs.z = tmax;
  601.     }
  602.   Object->Bounds.Lower_Left = mins;
  603.   VSub(Object->Bounds.Lengths, maxs, mins);
  604. #endif
  605.   }
  606.